; drive-thru_simulator.nlogo;; a model which represents the flow of a drive-thru;; Liam Whitenack; 02/22/2021; CDS 492; Spring 2022​globals[ x-coordinates ; the x-coordinate of a chosen car y-coordinates car-num ; total number of cars order_item ; placeholder for item waiting-to-join-line dto-1-switch dto-2-switch lane​ a​ cars-lost​ product-id​ bagged-orders​ new-car-at-window? started-at-window cars-served avg-window-time​ started-bagging bagging-time​ started-distributing distributing-time​ food-waiting-on drinks-waiting-on shakes-waiting-on num-employees​ worked-#s ; the list of products queued and chosen as a task finished-#s ; the list of completed products​ seed]​breed [cars car]breed [products product]breed [employees employee]breed [bags bag]​cars-own[ species ; can be a car or an item​ switch​ ; item attributes ready? bagged?​ ; car attributes carid food drinks shakes full_order num-food num-drinks num-shakes​ ordered? started-to-order order-time finished-ordering started-to-pay pay-time finished-paying​ paid? finished?]​products-own[ ;how long the product takes to prepare food-prep-time drinks-prep-time shakes-prep-time​ ;the tick number that the product starts to be prepared on food-prep-start-time drinks-prep-start-time shakes-prep-start-time​ ;product's id item-id]​bags-own [bag# bag-label]​employees-own[ task-time task-start product-#]​to-report id-products [x y] let product-list [self] of products-on patch x y let product-id-list [] foreach product-list [product-in-agentset -> set product-id-list lput [who] of product-in-agentset product-id-list] report product-id-listend​to-report product-types [product-ids] let products# [] foreach product-ids [ id -> set products# lput [item-id] of turtle id products# ] report products#end​to-report remove-first [element alist] let in? false let i 0 foreach alist [ x -> if x = element [ set in? true ] if not in? [set i i + 1] ] set alist remove-item i alist report alistend​to-report remove-list [list2 list1] let result filter [ x -> not member? x list2 ] list1 report resultend​to-report everything-ready? [food-needed drinks-needed shakes-needed food-ready drinks-ready shakes-ready] let continue? false​ let num-prepared 0​ foreach food-needed [ needed -> if member? needed food-ready [set num-prepared num-prepared + 1 set food-ready remove-first needed food-ready]​ set continue? (num-prepared = length food-needed) ] if length food-needed = 0 [set continue? true]​ if continue? [ set num-prepared 0 foreach drinks-needed [ needed -> if member? needed drinks-ready [set num-prepared num-prepared + 1 set drinks-ready remove-first needed drinks-ready]​ set continue? (num-prepared = length drinks-needed) ] if length drinks-needed = 0 [set continue? true] ]​ if continue? [ set num-prepared 0 foreach shakes-needed [ needed -> if member? needed shakes-ready [set num-prepared num-prepared + 1 set shakes-ready remove-first needed shakes-ready]​ set continue? (num-prepared = length shakes-needed) ] if length shakes-needed = 0 [set continue? true] ]​​ report continue?end​to-report product-with-id [id products-ready] let product-who [] foreach products-ready [ product-ready -> ;[who] of products with [item-id = 1 and pxcor = 8] ]end​to setup ifelse set-seed [ set seed 42 ] [ set seed random 255 ] random-seed seed set new-car-at-window? true set started-bagging 0 set started-distributing 0 clear-all setup-patches setup-employees reset-ticks monitor add-carend​to go step ;if turtle 66 != nobody [inspect turtle 66 stop]end​to test repeat n [step]end​to step spawn-cars drive tick order pay-for-order prep bag-products hand-out monitor​end​to monitor ifelse any? turtles-on patch 13 12 [ ifelse not member? (one-of [who] of turtles-on patch 13 12) ([bag#] of turtles-on patch 6 12) [ set food-waiting-on (remove-list [item-id] of turtles-on patch -5 4 one-of [food] of turtles-on patch 13 12) set drinks-waiting-on (remove-list [item-id] of turtles-on patch 0 4 one-of [drinks] of turtles-on patch 13 12) set shakes-waiting-on (remove-list [item-id] of turtles-on patch 5 4 one-of [shakes] of turtles-on patch 13 12) if food-waiting-on = [] and drinks-waiting-on = [] and shakes-waiting-on = [] [ set food-waiting-on "food must be bagged" set drinks-waiting-on "drinks must be bagged" set shakes-waiting-on "shakes must be bagged" ] ] [ set food-waiting-on "order must be distributed" set drinks-waiting-on "order must be distributed" set shakes-waiting-on "order must be distributed" ] ] [ set food-waiting-on [] set drinks-waiting-on [] set shakes-waiting-on [] ] if ticks = 0 [set num-employees count employees] if not pw and any? turtles-on patch 9 -5 [ask one-of turtles-on patch 9 -5 [die]] if pw and not any? turtles-on patch 9 -5 [set num-employees 0] ifelse pw [set a 1] [set a 0] if count employees != num-dto + a + num-food-prep-employees + num-drinks-prep-employees + num-shakes-prep-employees + 1 [ setup-employees setup-patches set num-employees count employees ] ;if not (num-employees = count employees) ;[ ; setup-employees ; setup-patches ; set num-employees count employees ;]​end​to order set dto-1-switch false set dto-2-switch false ask cars with [pycor = -8 and (pxcor < 0)] [ if order-time = 0 [set order-time random-normal avg-order-time (avg-order-time / 2)] if started-to-order = 0 [set started-to-order ticks] if ticks > order-time + started-to-order [ if not ordered? [ if pxcor = -10 [set dto-1-switch true] if pxcor = -14 [set dto-2-switch true] set finished-ordering ticks set ordered? true set color green - 2 ]​ ] ] if dto-1-switch [ set lane 1 queue-items set dto-1-switch false ] if dto-2-switch [ set lane 2 queue-items set dto-2-switch false ]end​to queue-items​ let food-queued one-of [food] of cars with [pycor = -8 and ((pxcor = -10 and lane = 1) or (pxcor = -14 and lane = 2))] let drinks-queued one-of [drinks] of cars with [pycor = -8 and ((pxcor = -10 and lane = 1) or (pxcor = -14 and lane = 2))] let shakes-queued one-of [shakes] of cars with [pycor = -8 and ((pxcor = -10 and lane = 1) or (pxcor = -14 and lane = 2))]​ foreach food-queued [ food-item -> set product-id food-item add-food-to-queue ]​ foreach drinks-queued [ drinks-item -> set product-id drinks-item add-drinks-to-queue ]​ foreach shakes-queued [ shakes-item -> set product-id shakes-item add-shakes-to-queue ]end​to add-food-to-queue create-products 1 [ set food-prep-time random-normal avg-food-prep-time (avg-food-prep-time / 2) set food-prep-start-time 0 set item-id product-id set size 2.5 set color yellow set shape "square" setxy -5 -6 ]end​to add-drinks-to-queue create-products 1 [ set drinks-prep-time random-normal avg-drinks-prep-time (avg-drinks-prep-time / 2) set drinks-prep-start-time 0 set item-id product-id set size 2.5 set color yellow set shape "circle" setxy 0 -6 ]end​to add-shakes-to-queue create-products 1 [ set shakes-prep-time random-normal avg-shakes-prep-time (avg-shakes-prep-time / 2) set shakes-prep-start-time 0 set item-id product-id set size 2.5 set color yellow set shape "triangle" setxy 5 -6 ]end​to prep if any? turtles-on patch -5 -6 ;and false [prepare-products turtles-on patch -5 -6 turtles-on patch -5 -1 avg-food-prep-time] if any? turtles-on patch 0 -6 ;and false [prepare-products turtles-on patch 0 -6 turtles-on patch 0 -1 avg-drinks-prep-time] if any? turtles-on patch 5 -6 ;and false [prepare-products turtles-on patch 5 -6 turtles-on patch 5 -1 avg-shakes-prep-time]end​to prepare-products [products-set employees-set avg-task-time]​​ let product-list sort [self] of products-set let product-list-ids [] foreach product-list [product-agent -> set product-list-ids lput [who] of product-agent product-list-ids] let product-list-unstarted-ids sort product-list-ids set product-list-ids sort product-list-ids let started-#s []​ ;create a list of employees let employee-list sort [self] of employees-set ; remove every product being worked on foreach employee-list [ employee-agent -> set started-#s lput ([product-#] of employee-agent) started-#s ]​ set product-list-unstarted-ids remove-list started-#s product-list-unstarted-ids​​ ; if any employees have a task ;if length product-list-unstarted-ids > 0 ;[ ; set the task start time of each employee and the predetermined preparation time foreach employee-list [ employee-agent -> if length product-list-unstarted-ids > 0 [ if ([product-#] of employee-agent) = 0; if they haven't started a task yet [ if length product-list-unstarted-ids != 0 [ ask employee-agent [ set task-start ticks set task-time abs random-normal avg-task-time avg-task-time / 2 set product-# min product-list-unstarted-ids ] set product-list-unstarted-ids remove (min product-list-unstarted-ids) product-list-unstarted-ids ] ] ]​ if (([task-start] of employee-agent) + ([task-time] of employee-agent)) < ticks and ([product-#] of employee-agent != 0) [ ask turtle [product-#] of employee-agent [ set heading 0 forward 10 set color orange ]​ ask employee-agent [ set task-start 0 set product-# 0 set task-time 0 ] ] ]end​to bag-products​ ; identify the numbers of the cars in line and put them into a list let cars-agentset [self] of cars with [ordered?] let cars-list [] if not (cars-agentset = []) and count products > 0 [ if started-bagging = 0 [ set started-bagging ticks set bagging-time random-normal avg-bagging-time (avg-bagging-time / 4) ] if ticks - started-bagging > bagging-time [ foreach cars-agentset [car-in-line -> set cars-list lput [who] of car-in-line cars-list] if not (cars-list = []) [​ let food-ready 0 let drinks-ready 0 let shakes-ready 0​ let waiting-to-be-bagged length cars-list​ set cars-list remove-list bagged-orders cars-list​ while [not (cars-list = []) and ticks - started-bagging > bagging-time + (((waiting-to-be-bagged - length cars-list) / 4) * avg-order-time)] [ set food-ready product-types id-products -5 4 set drinks-ready product-types id-products 0 4 set shakes-ready product-types id-products 5 4​ let food-needed [food] of turtle min cars-list let drinks-needed [drinks] of turtle min cars-list let shakes-needed [shakes] of turtle min cars-list​​​ ; check to make sure that everything needed in the order is prepared ifelse everything-ready? food-needed drinks-needed shakes-needed food-ready drinks-ready shakes-ready [ ;remove all items being prepared from the ready spots​​ if length food-needed > 0 [ foreach food-needed [ needed -> ask turtle one-of [who] of products with [item-id = needed and pxcor = -5 and pycor > 0] [die] ] ]​ if length drinks-needed > 0 [ foreach drinks-needed [ needed -> ask turtle one-of [who] of products with [item-id = needed and pxcor = 0 and pycor > 0] [die] ] ]​ if length shakes-needed > 0 [ foreach shakes-needed [ needed -> ask turtle one-of [who] of products with [item-id = needed and pxcor = 5 and pycor > 0] [die] ] ]​​​ ; add a new bag to be handed out create-bags 1 [ set shape "box" set color brown set size 2.5 setxy 6 12 set bag# min cars-list set bag-label [label] of turtle min cars-list ;set label-color black ]​​ set bagged-orders lput min cars-list bagged-orders​ set cars-list [] set started-bagging 0 ] [ set cars-list remove-item 0 cars-list ] ] ] ] ]​​​end​to pay-for-order ask cars with [(pxcor = 13 and pycor = -4 and pw) or (pxcor = 13 and pycor = 12 and not pw)] [ if pay-time = 0 [set pay-time random-normal avg-pay-time (avg-pay-time / 2)] if pay-time < 0 [set pay-time (-1 * pay-time)] if started-to-pay = 0 [set started-to-pay ticks] if ticks > pay-time + started-to-pay [ if not paid? [ set paid? true set color red ] ] ]end​to hand-out if any? turtles-on patch 13 12 and any? turtles-on patch 6 12 [​ let id 0 if [paid?] of one-of turtles-on patch 13 12 [ ;create a list of the bagged orders let bagged-list [self] of turtles-on patch 6 12 ;identify the car at the window let car-at-window one-of [who] of turtles-on patch 13 12 ;check every bag to make sure we have the correct bag foreach bagged-list [ bagged-turtle -> ;if the bag matches the car if [bag#] of bagged-turtle = car-at-window [ if started-distributing = 0 [ set started-distributing ticks set distributing-time random-normal avg-distributing-time (avg-distributing-time / 2) if distributing-time < 10 [set distributing-time 10] ]​ if ticks - started-distributing > distributing-time [ ;kill the car at the window ask turtles-on patch 13 12 [die] ;identify the bag that needs to die set id [who] of bagged-turtle ] ]​ if not (id = 0) [ ask turtle id [die] set id 0 set new-car-at-window? true set cars-served cars-served + 1 set avg-window-time avg-window-time + ((ticks - started-at-window) - avg-window-time) / cars-served set started-distributing 0 ] ] ] ]end​to setup-patches set worked-#s [] set finished-#s [] set bagged-orders [] ; make grass ask patches[ set pcolor (random 3 + 61) ] repeat 20 [ diffuse pcolor 0.25 ] ; make the road ask patches with [pxcor > -16 and pxcor < 16 and pycor > -14] [set pcolor black] ask patches with [pxcor < 16 and pycor > 12] [set pcolor black] ; make the building ask patches with [pxcor > -8 and pxcor < 12 and pycor > -10] [set pcolor gray] ; create DTO spots ifelse num-dto = 1 ; if there is one DTO [ ; left limit right limit upper limit lower limit ask patches with [pxcor > -9 and pxcor < -7 and pycor < -5 and pycor > -10] [set pcolor blue] ] ; if there is more than one DTO [ ; left limit right limit upper limit lower limit ask patches with [pxcor > -9 and pxcor < -7 and pycor < -5 and pycor > -10] [set pcolor blue] ask patches with [pxcor > -13 and pxcor < -11 and pycor < -5 and pycor > -10] [set pcolor blue] ] ; add a payment window if turned on if pw [ask patches with [pxcor > 10 and pxcor < 12 and pycor < -2 and pycor > -7] [set pcolor green - 2]]​ ; add a window for handing out food ask patches with [pxcor > 10 and pxcor < 12 and pycor < 14 and pycor > 9] [set pcolor red]end​to setup-employees​​ ask employees [die]​ if pw [ create-employees 1 [ set color green - 2 setxy 9 -5 set shape "person" set size 4 ] ]​ create-employees 1 ; window employee [ set color red setxy 9 11 set shape "person" set size 4 ]​ create-employees 1 ; dto lane 1 employee [ set color blue setxy -1 16 set shape "person" set size 4 ]​ if num-dto = 2 [ create-employees 1 ; dto lane 2 employee [ set color blue setxy -5 16 set shape "person" set size 4 ] ]​ repeat num-food-prep-employees [ create-employees 1 ; food prep employee [ set color yellow setxy -5 -1 set shape "person" set size 4 set label num-food-prep-employees ] ]​ repeat num-drinks-prep-employees [ create-employees 1 ; food prep employee [ set color yellow setxy 0 -1 set shape "person" set size 4 set label num-drinks-prep-employees ] ]​ repeat num-shakes-prep-employees [ create-employees 1 ; food prep employee [ set color yellow setxy 5 -1 set shape "person" set size 4 set label num-shakes-prep-employees ] ]​ ask employees [set task-start 0]​ ;repeat num-positionless-prep-employees ;[ ; ifelse not any? employees-on patch -5 -1 ; [ ; create-employees 1 ; food prep employee ; [ ; set color yellow + 2 ; setxy -5 -1 ; set shape "person" ; set size 4 ; set label num-positionless-prep-employees ; ] ; ] ; [ ; ifelse not any? employees-on patch -5 -1 ; [ ; create-employees 1 ; shakes prep employee ; [ ; set color yellow + 2 ; setxy 0 -1 ; set shape "person" ; set size 4 ; set label num-positionless-prep-employees ; ] ; ] ; [ ; create-employees 1 ; positionless prep employee ; [ ; set color yellow + 4 ; setxy 5 -1 ; set shape "person" ; set size 4 ; ] ; ] ; ] ;]​end​to spawn-cars ; if the number of ticks is divisible by 10 if ticks - (floor (ticks)) = 0 [ let join? (random 10000) / 10000 <= CARS_PER_TICK if join? [ if not (((cars-fit - waiting-to-join-line) / cars-fit) > (random 1000) / 1000) [ set cars-lost cars-lost + 1 set join? false ] ] ; if the number generated is lower than or equal to the car join rate, spawn a car if join? or waiting-to-join-line > 0 [ ; if there's not someone blocking the spot to join ifelse not any? cars-on patch -19 15 [ add-car ; [set cars-lost cars-lost + 1] ; if there's more cars waiting, spawn them if waiting-to-join-line > 0 and not join? [ ; there's one less car waiting set waiting-to-join-line waiting-to-join-line - 1 ] ] [ if join? [ ; if there's no spot to join, add them up set waiting-to-join-line waiting-to-join-line + 1 ] ] ] ]​​end​to add-car create-cars 1 [ set car-num car-num + 1 set label car-num set breed cars set size 2.5 set shape "car" set color blue setxy -19 15 set paid? false set ordered? false set started-to-order 0 set order-time 0 set pay-time 0 set started-to-pay 0 set switch false set food [] set drinks [] set shakes [] set num-food 0 set num-drinks 0 set num-shakes 0 while [num-food < 1 and num-drinks < 1 and num-shakes < 1] [ set num-food random-normal avg-food 2 ;while [num-food < 0] [set num-food random-normal avg-food 2] set num-drinks random-normal avg-drinks 2 ;while [num-food < 0] [set num-food random-normal avg-food 2] set num-shakes random-normal avg-shakes 2 ;while [num-food < 0] [set num-food random-normal avg-food 2] ] repeat num-food [set food lput random 10 food] repeat num-drinks [set drinks lput random 10 drinks] repeat num-shakes [set shakes lput random 10 shakes] ]end​to drive if true [ ask cars with [shape = "car"] [ ; cars start to advance in line if pycor = 15 and pxcor < -10.9 [ set heading 90 ; set the starting heading if not any? cars-on patch-ahead 5 and not any? cars-on patch-ahead 4 and not any? cars-on patch-ahead 3 and not any? cars-on patch-ahead 2 and not any? cars-on patch-ahead 1 [​ ; if there's room for you ahead, move forward. if not (count cars-on patches with [pxcor = -10] > 5) or pxcor != -14 [forward 1] ] ]​ ; cars continue to advance in line, switching lanes if one is shorter than the other if pycor > -10.9 [ if (pxcor = -10) or (pycor < 15 and pxcor = -14) [ set heading 180 ; start moving downwards​ ; if there are no cars ahead if not any? cars-on patch-ahead 5 and not any? cars-on patch-ahead 4 and not any? cars-on patch-ahead 3 and not any? cars-on patch-ahead 2 and not any? cars-on patch-ahead 1 [ ; if there are less than 4 cars in the line on pycor = -11, don't join it. Otherwise, move forward. if ((count cars-on patches with [pycor = -11] < 4) and ordered?) or pycor != -8 [forward 1]​ ; if there are two drive-thru lanes, consider switching if num-dto = 2 [ if count cars-on patches with [pxcor = -10 and pycor < 14] > count cars-on patches with [pxcor = -14 and pycor < 14] + 1 and pxcor = -10 [ if pycor > 7 [ set heading 270 if not any? cars-on patch-ahead 4 [forward 4] set heading 180 ] ] ] ] ] ]​ ; cars, once they are finished ordering, begin advancing to the payment window if pycor = -11 and pxcor < 12.1 [​ ; start moving to the right set heading 90​ ; if there are no cars ahead if not any? cars-on patch-ahead 5 and not any? cars-on patch-ahead 4 and not any? cars-on patch-ahead 3 and not any? cars-on patch-ahead 2 and not any? cars-on patch-ahead 1 [ ; if there is not a car ahead, you can move forward (accounting for the potential change of direction ahead) if (not any? cars-on patches with [pxcor = 13 and pycor > -12 and pycor < -7]) or pxcor != 10 [forward 1] ] ]​ ; start moving towards the window and payment area if pxcor = 13 and pycor < 12 [ if not any? cars-on patch-ahead 5 and not any? cars-on patch-ahead 4 and not any? cars-on patch-ahead 3 and not any? cars-on patch-ahead 2 and not any? cars-on patch-ahead 1 [ set heading 0 if paid? or (pycor < -4 or (pycor < 12 and not pw)) [forward 1] ;if (pycor = -4 and pw) or (pycor = 12 and not pw) [pay-for-order] ] ]​ if any? turtles-on patch 13 12 and new-car-at-window? = true [ set started-at-window ticks set new-car-at-window? false ]​​ ; this is mostly for testing, uncomment the below line to make the cars dissapear after they leave the window ;if pxcor > 12 and pycor > 12 [die] ] ] if not any? turtles-on patch 13 12 [ set started-at-window ticks + 1 ]end(a general understanding of what the model is trying to show or explain)
(what rules the agents use to create the overall behavior of the model)
(how to use the model, including a description of each of the items in the Interface tab)
(suggested things for the user to notice while running the model)
(suggested things for the user to try to do (move sliders, switches, etc.) with the model)
(suggested things to add or change in the Code tab to make the model more complicated, detailed, accurate, etc.)
(interesting or unusual features of NetLogo that the model uses, particularly in the Code tab; or where workarounds were needed for missing features)
(models in the NetLogo Models Library and elsewhere which are of related interest)
(a reference to the model's URL on the web if it has one, as well as any other necessary credits, citations, and links)